/***************************************************************************
													parser.cpp  -  description
														 -------------------
		begin                : Mon May 6 2002
		copyright            : (C) 2002 by Michiel Visser
		email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/*
TyleParser
	Sequence
		Assign(=Expr)(RR)
			Modify (=?, .?)
				Iterators(RR)
					Param (<...>...)
						Switch(RR) (=while,switch)
							Binary
								Unary
									Index
										Operand
											Block
												{ finishList }
													Assign
												( finishSequence )
													Sequence
														Assign
												{: finishMap }
													finishMap
											String
											Number
											Var
*/
 
#include "parser.h"
#include "regexp.h"

#include<math.h>

using namespace Expression;

static const int DYNAMIC_BITMASK_DYN = 1;
static const int DYNAMIC_BITMASK_SHAREDLITERAL = 2;
static const int DYNAMIC_BITMASK_INSIDE_REF = 4;

void TyleParser::consolidateSharedLiterals(EPtr &p)
{
	if (m_sharedLiteralDefined.size()) {
		if (m_dynamic&DYNAMIC_BITMASK_DYN) {
			throw("#[num] can only be used in constant expressions");
		}
		_ASSERT(m_dynamic&DYNAMIC_BITMASK_SHAREDLITERAL);
		m_dynamic&=~DYNAMIC_BITMASK_SHAREDLITERAL;
		EPtr r;
		new (r) SharedLiteralsScopeExpr(p,m_sharedLiteralDefined.size());
		m_sharedLiteralDefined.clear();
		p = r;
	}
	m_sharedLiteralUnreferenced.clear();
}

inline bool isIdentifier(char c)
{
	return (c>='a'&&c<='z') || (c>='A'&&c<='Z') || c=='_';
}

TyleParser::TyleParser(const unsigned char *pos, EPtr &result, void *returnObjectSpace):
	m_pos(pos),
	m_c(0),
	m_line(1),
	m_s(0x100000),
	m_dynamic(0)
{
	session.m_funcLevel=0;
	try {
		nextChar();
		nextWord();
		result = parseSequence();
		consolidateSharedLiterals(result);
		skipSpaces();
		if (m_s[0]!='\0') {
			throw("Could not parse completely.");
		}
	}
	catch (const char *error) { 
		if (returnObjectSpace)
		{	char s[256];
	 		sprintf(s,"Parse error at line %i: %s",m_line, (char *)error);
	 		new (returnObjectSpace) StringObject(s);
		}
		result=EPtr();
	}
}

void TyleParser::nextChar()
{ 
	while (m_pos[0]=='/')
	{ if (m_pos[1]=='*')
		{ for (m_pos+=2; *m_pos!=0; m_pos++)
			{ if (*m_pos==10) m_line++;
				if (*m_pos=='*' && m_pos[1]=='/') { m_pos+=2; break; }
			}
		} else if (m_pos[1]=='/')
		{ for (m_pos+=2; *m_pos!=0; m_pos++)
			{ if (*m_pos==10) { m_line++; m_pos++; break; }
			}
		} else break;
	}
	m_c=*(m_pos++);
	if (m_c==10) m_line++;
}

void TyleParser::nextWord()
{ int pos = 0;
#define SCP { storeChar(pos); nextChar(); }
#define SRCP { storeChar(pos); nextRawChar(); }
	skipSpaces();
	if (m_c=='\"')
	{ do
		{ if (m_c==0) throw("String not finished.");
			else if (m_c<32) { nextRawChar(); continue; }
			else if (m_c=='\\')
			{	nextRawChar();
	    	m_c=parseEscapeChar(m_c,m_pos);
			}
			SRCP;
		} while (m_c!='\"');
		m_strlen=pos-1;
		nextChar();
	}
	else if (m_c=='\'')
	{	m_c='\"';
	  do
		{ if (m_c==0) throw("String not finished.");
			SRCP;
		} while (m_c!='\'');
		m_strlen=pos-1;
		nextChar();
	}
	else if (m_c=='`')
	{ do
		{ if (m_c==0) throw("Regexp not finished.");
			else if (m_c<32) { nextRawChar(); continue; }
			else if (m_c=='\\')
			{	SRCP;
				if (m_c==0) throw("Escape sequence not finished.");
			}
			SRCP;
		} while (m_c!='`');
		m_strlen=pos-1;
		unsigned char mask=0;
		for(;;)
		{ nextRawChar();
			if      (m_c=='i') mask|=1;
			else if (m_c=='m') mask|=2;
			else if (m_c=='o') mask|=4;
			else break;
		}
		m_s[pos++]=mask;
	}
	else if (m_c>' ')
	{ short omc=m_c;
		SCP
		switch(omc)
		{
		case '#': if (m_c>='0' && m_c<='9') do { SCP; } while (m_c>='0' && m_c<='9'); else while(m_c!=';' && m_c!=',' && m_c!=0) SCP; break;
	  case '.': if (m_c=='!'||m_c=='?'||m_c=='*'||m_c=='@') SCP else if (m_c=='.' && m_pos[0]=='.') {SCP; SCP;} break; 
	  case ':': if (m_c=='!'||m_c=='@') SCP else if (m_c=='+'||m_c=='-') { SCP if (m_c=='=') SCP } break;
	  case '|': if (m_c=='|') SCP           if (m_c=='=') SCP break;
	  case '&': if (m_c=='&') SCP           if (m_c=='=') SCP break;
	  case '+': if (m_c=='<'||m_c=='>') SCP if (m_c=='=') SCP break;
	  case '<': if (m_c=='<') SCP           if (m_c=='=') SCP break;
	  case '>': if (m_c=='>') SCP           if (m_c=='=') SCP break;
	  case '-': if (m_c=='>') SCP else { if (m_c=='*') SCP if (m_c=='=') SCP break; }
	  case '%': if (m_c=='=') { SCP break; } // else fall through
	  case '?': if (m_c=='!') SCP break;
	  case '@': if (m_c=='@') SCP break;
	  case '*': if (m_c=='!') { SCP break; } // else fall through
	  case '^':
	  case '/': if (m_c=='=') SCP break;
	  case '[': if (m_c==':' && m_pos[0]=='-') SCP break;
	  case '=': if (m_c=='=' || m_c=='>' ) SCP break;
	  case '!': if (m_c=='=' && m_pos[0]!='=' && m_pos[0]!='>') SCP break;
	  case '{': if (m_c==':') SCP break;
		default:
			if ( omc>='0' && omc<='9')
			{	if (omc=='0' && (m_c=='x'||m_c=='X'))
				{	do { SCP; } while ((m_c>='0' && m_c<='9') || (m_c>='a' && m_c<='f') || (m_c>='A' && m_c<='F'));
				}
				else if (omc=='0' && (m_c=='b'||m_c=='B'))
				{	do { SCP; } while ((m_c=='0' || m_c=='1'));
				}
				else if (omc=='0' && (m_c=='o'||m_c=='O'))
				{	do { SCP; } while ((m_c>='0' && m_c<='7'));
				}
				else
				{	while (m_c>='0' && m_c<='9') SCP;
					if (m_c=='.' && m_pos[0]>='0' && m_pos[0]<='9')
					{	SCP; 
						do { SCP; } while (m_c>='0' && m_c<='9');
					}
					if (m_c=='e'||m_c=='E')
					{	SCP;
						if (m_c=='-'||m_c=='+') SCP;
						while (m_c>='0' && m_c<='9') SCP;
					}
				}
			}
			else if ( isIdentifier(omc) )
			{ while ((m_c>='0' && m_c<='9') || isIdentifier(m_c) ) SCP;
			}
			break;
		}
	}
	m_s[pos]=0;
#undef SCP
}

bool TyleParser::parseWord(const char *word)
{
	if (strcmp(m_s,word)!=0)
	{ return false;
	}
	nextWord();
	return true;
}

EPtr TyleParser::parseString()
{
	if (m_s[0]!='\"' && m_s[0]!='`') return EPtr();
	{
		EPtr res;
		if (m_s[0]=='`')
		{ (NEW_CONSTEXPR(res) RegExpObject(((unsigned char *)m_s)+1,m_strlen,((unsigned char *)m_s)[m_strlen+1]))->parse();
		}
		else
		{ NEW_CONSTEXPR(res) StringObject(((char *)m_s)+1,m_strlen);
		}
		nextWord();
		return res;
		}
}

void TyleParser::addStructElem(List<StructElem> &ses, TypeVar parent, String name, TypeVar type, TypeFunc *func)
{
	int i=ses.length();
	new (ses.end()) StructElem(name, type);
	if (func)
	{	new (*structing.funcs) TypeToStructElemAssignment (parent, i, func);
		structing.funcs++;
	}
}

TypeVar TyleParser::parseType(/*out*/ TypeFunc *&funcOut)
{
	TypeVar p(parseStructConcat(funcOut));

	if (parseWord("=>"))
	{ 
		TypeVar np;
		new (np) TypeVal();
		if (!!funcOut)
		{ new (*structing.funcs) TypeToStructElemAssignment (np, 0, funcOut);
			structing.funcs++;
		}
		TypeVar p2(parseType(funcOut));
		if (!!funcOut)
		{ new (*structing.funcs) TypeToStructElemAssignment (np, -1, funcOut);
			structing.funcs++;
			funcOut=0;
		}
		List<StructElem> elems;
		new (elems) StructElem(String(), p);
		np->set(elems, false);
		np->m_childType=p2;
		return np;
	}
	return p;
}
TypeVar TyleParser::parseStructConcat(/*out*/ TypeFunc *&funcOut)
{
	TypeVar p(parseTypeAssignment(funcOut));
	if (parseWord("+"))
	{
		if (!funcOut)
		{	funcOut = new TypeSelection(p);
			p = TypeVar();
		}
		do 
		{	TypeFunc *func2;
			TypeVar p2(parseTypeAssignment(func2));
			if (!func2)
			{	func2 = new TypeSelection(p2);
			}
			funcOut = new StructConcat(funcOut,func2);
		} while (parseWord("+"));
	}
	return p;
}

TypeVar TyleParser::parseTypeLeftVal(/*out*/ TypeFunc *&funcOut)
{
	funcOut=0;
	TypeVar p;
	String name((char *)m_s);	
	nextWord();
	p = session.findType(name);
	if (!p) {
		funcOut = new PreTypeSelection(name);
		//old!-> todo p = session.newTypeEntry(name);
	}
	if (parseWord(".")) { 
		if (!!p)
			funcOut = new TypeSelection(p);
		p=TypeVar();
		do
		{	String name((char *)m_s);	//todo: add word check
			nextWord();
			funcOut=new StructIndexing(funcOut,name);
		} while (parseWord("."));
	}
	return p;
}
TypeVar TyleParser::parseTypeAssignment(/*out*/ TypeFunc *&funcOut)
{
	funcOut=0;
	if ( isIdentifier(m_s[0]))
	{ TypeVar p = parseTypeLeftVal(funcOut);
		if (parseWord("="))
		{	TypeFunc *func2;
			if (!funcOut)
				funcOut = new TypeSelection(p); 
			TypeVar p2 (parseType(func2));
			if (!!p2)
				func2 = new TypeSelection(p2); 
/*			if (!!p)
			{	new (*structing.funcs) TypeAssignment(funcOut, func2);
				funcOut=0;
			}
			else
				funcOut = new TypeAssignment(funcOut, func2);*/
			funcOut = new TypeAssignment(funcOut, func2);
			return TypeVar();
		}
		return p;
	}
	else return parseTypeLiteral(funcOut);
}

TypeVar TyleParser::parseTypeLiteral(TypeFunc *&funcOut)
{
	funcOut = 0;
	TypeVar p;
	List<StructElem> ses;
	if (parseWord("{"))
	{	bool allowDynamicElems=false;
		new (p) TypeVal(allowDynamicElems);
		while (!parseWord("}"))
		{	
			if (parseWord("..."))
			{ allowDynamicElems=true;
			  if (!parseWord("}")) throw("Expected '}' after '...'");
			  break;
		  }
	  	TypeFunc *subFunc;
			TypeVar subType(parseTypeSelection(subFunc));
	  	if (!isIdentifier(m_s[0])) throw("Expected identifier.");
			String m_sc((char *)m_s);
			addStructElem(ses,p,m_sc,subType,subFunc);
	  	nextWord();
	  	if (parseWord("}")) break;
	  	if (!parseWord(","))
	  	{ throw("Expected ',' or '}'.");
	  	}
		}
		p->set(ses,allowDynamicElems);
	}
	else if (parseWord(":"))
	{ p=parseType(funcOut);
		TypeVar np;
		new (np) TypeVal(p);
		if (!!funcOut)
		{ new (*structing.funcs) TypeToStructElemAssignment (np, -1, funcOut);
			structing.funcs++;
			funcOut=0;
		}
		return np;
	}
	else if (parseWord("("))
	{ p=parseType(funcOut);
		if (!parseWord(")"))
		{ throw("Expected ')'.");
		}    
	}
	return p;
}


TypeVar TyleParser::parseTypeSelection(TypeFunc *&funcOut, bool *pFound)
{
	funcOut=0;
	TypeVar t;
	bool found = parseWord("[");
	if (pFound)
		*pFound = found;
	if (found) {
		t=parseType(funcOut);
		if (!parseWord("]")) throw("Missing ']' of Type definition.");
	}
	return t;
}
static String concatStrings(List<String> &txt)
{
	int l=0;
	FOREACH(List<String>,txt,i) l+=i->length();
	String str(l+1);
	char *sp=str;
	FOREACH(List<String>,txt,i)
	{ int l=i->length();
		memcpy(sp,(char *)*i,l);
		sp+=l;
	}
	*sp=' ';
	return str;
}
EPtr TyleParser::parseTypeSelectionExpr()
{
	EPtr res;
	List<TypeFunc> structFuncs;
	structing.funcs=&structFuncs;
	TypeFunc *func;
	bool found;
	TypeVar type(parseTypeSelection(func,&found));
	if (!!type || (!func && found))
	{	func = new TypeSelection(type);
	}
	if(func)
	{ new (res) TypeSelectionExpr(func,structFuncs);
	}
	return res;
}

EPtr TyleParser::parseVar()
{
	EPtr res;
#ifdef TYLE_V2
	bool single=parseWord("$");
	if (single || parseWord("$$"))
	{
		m_dynamic=1;
		bool isMultiple = session.m_funcLevel==0 || session.getDollarMultiplicity(session.m_funcLevel) == 2;
		if (!single && !isMultiple)
			throw("'$$' only for parameter set and not for a single parameter.");
		res = single 
			? ( isMultiple ? session.m_exprDollarM2S : session.m_exprDollarS2S )
			: session.m_exprDollarM2M;
#else
	if (parseWord("$"))
	{ int depth;
		m_dynamic|=DYNAMIC_BITMASK_DYN;
		if (m_s[0]>='a'&&m_s[0]<='z')
		{ int d=m_s[0]-'a';
	    int l=session.m_funcLevel+d;
	    if (d<3 && l<4)
	    	res = session.m_exprAbsDollarVars[d][l];
	    else
	    	new (res) DollarVarExpr(l, int('a')+d);
			nextWord();
		} 
		else if (parseDigits(m_s,depth)>0)
		{ if (depth==0) throw("Variable $0 is not valid, use: $,$2,$3...");
			nextWord();
			if (depth<4)
				res = session.m_exprDollarVars[depth-1];
			else
				new (res) DollarVarExpr(depth-1,int('0')+depth);
		}
		else
			res = session.m_exprDollarVars[0];
#endif
	}
	else
	{ bool localFind = parseWord(".");
		unsigned char c=m_s[0];
		if ( isIdentifier(c) )
		{ m_dynamic|=DYNAMIC_BITMASK_DYN;
			res = session.findOrInsert((char *)m_s,localFind);
			nextWord();
		}
		else if (localFind)
			throw("Variable expected after '.'.");
	}
	return res;
}

int TyleParser::parseDigits(const unsigned char *p, /*out */int &result, int base)
{
	result=0;
	int count=0;
	for(;;)
	{ int v = hexDigitToValue(*p);
	  if (v<0 || v>=base) break;
	  result = result * base + v;
		count++;
		p++;
	}
	return count;
}

const unsigned char *TyleParser::parseInt(const unsigned char *p, int &base, /*out*/ int &result)
{	
	int sign=1;
	if (*p=='-') { p++; sign=-1; }
	if (*p<'0' || *p>'9') { result=0; return 0; }
	if (*p=='0' && p[1]!=0 && p[1]!='.' && (p[1]<'0' || p[1]>'9'))
	{
		if (p[1]=='x' || p[1]=='X')      { p+=2; base=16; }
		else if (p[1]=='o' || p[1]=='O') { p+=2; base=8; }
		else if (p[1]=='b' || p[1]=='B') { p+=2; base=2; }
		else { result=0; return 0; }
	} else base=10;
	p+=parseDigits(p,result,base);
	result*=sign;
	return p;
}
	
EPtr TyleParser::parseNumber()
{
	int result;
	int base;
	EPtr res;
	const unsigned char *p=m_s;
	p=parseInt(p, base, result);
	if (!p)
		return EPtr();
	int r2=0,c2=0,r3=0, isFloat=0;
	if (*p=='.')
	{	if ((c2=parseDigits(++p,r2,base))==0) { throw("Missing digits after '.'"); return EPtr(); }
		p+=c2;
		isFloat=1;
	}
	if (*p=='e'||*p=='E')
	{	int esign=1;
		if (p[1]=='-') { esign=-1; p++; }
		else if (p[1]=='+') p++;
		parseDigits(p+1,r3,10);
		r3*=esign;
		isFloat=1;
	}
	if (isFloat)
		NEW_CONSTEXPR(res) FloatObject((result + r2*pow(base,-c2))*pow(10,r3) );
	else
	{	if (result>=0 && result<4)
		res=session.m_exprNumbers[result];
	else
		NEW_CONSTEXPR(res) IntegerObject(result);
	}
		
	nextWord();
	return res;
}

EPtr TyleParser::parseHash()
{
	EPtr res;
	if (m_s[0]=='#') {	
		if (m_s[1]>='1' && m_s[1]<='9') {
			if (m_dynamic&DYNAMIC_BITMASK_DYN) {
				throw("#[num] can only be used in constant expressions");
			}
			int value;
			parseDigits((const unsigned char *)m_s+1,value,10);
			value--;
			nextWord();
//todo: remove sharedliterals completely, except count
			if (!(m_dynamic & DYNAMIC_BITMASK_INSIDE_REF)) {
				if (value>=m_sharedLiteralUnreferenced.size())
					m_sharedLiteralUnreferenced.resize(value+1,false);
				if (m_sharedLiteralUnreferenced[value])
					throw("#[num] used twice unreferenced");
				m_sharedLiteralUnreferenced[value] = true;
			}
			if (parseWord("=")) {
				if (value>=m_sharedLiteralDefined.size())
					m_sharedLiteralDefined.resize(value+1,false);
				else if (m_sharedLiteralDefined[value]==true)
					throw("#[num] already defined for this number");
				m_sharedLiteralDefined[value] = true;
				EPtr o=parseUnary();
				operCheck(o);
				if (m_dynamic&DYNAMIC_BITMASK_DYN)
					throw("Expr in #[num]=[expr] is not constant");
				new (res) SharedLiteralExprAssigned(value, o); 
			} else {
				if (m_sharedLiteralDefined.size()<=value || !m_sharedLiteralDefined[value] )
					throw("#[num] referenced, but not yet defined");
				new (res) SharedLiteralExpr(value); 
			}
			m_dynamic|=DYNAMIC_BITMASK_SHAREDLITERAL;
		}
		else {
			throw("Expected (positive) number after #");
		}
	}
	return res;
}

EPtr TyleParser::parseOperand()
{
	EPtr res, psd (parseTypeSelectionExpr() );

	if (!(res=parseBlock()))
	if (!(res=parseString()))
	if (!(res=parseNumber()))
	if (!(res=parseVar()))
		res=parseHash();

	if (!!psd) {
		((TypeSelectionExpr *)&*psd)->a=res;
		m_dynamic|=DYNAMIC_BITMASK_DYN;
		return psd;
	}
	else return res;
	
}

//#define RetObj(x) { EPtr res; new (res) x; return res; }

#define ENDFUNC }
#define PARSEFUNC(x) EPtr TyleParser::parse##x() {
#define BINARY_BEGIN \
	{ EPtr cur(father()); \
	if ( !cur ) return EPtr();\
	while (true) {
#define BINARY_END \
		else break; \
	} return cur; \
}
#define BINARY_BEGIN_RIGHT(x) \
	{ EPtr cur(x()); \
	if ( !cur ) return EPtr();
#define BINARY_END_RIGHT \
	return cur;\
}
#define CREATE_BIN_ITEM(x) { consolidateSharedLiterals(cur); m_dynamic|=DYNAMIC_BITMASK_DYN; EPtr prev(cur); EPtr f(father()); pcheck(f); new (cur) x(prev,f); }
#define NEXT_CREATE_BIN_ITEM(x) { nextWord(); CREATE_BIN_ITEM(x); }
#define NEXT_CREATE_SUB_BIN_ITEM(x) { session.m_funcLevel++; NEXT_CREATE_BIN_ITEM(x); session.m_funcLevel--;}

#define BINARY_ITEM(str,x) if (parseWord(str)) CREATE_BIN_ITEM(x)

EPtr TyleParser::parseIndependentBlock()
{	
	int oldyn = m_dynamic;
	m_dynamic = 0;
	EPtr res(parseBlock());
	if (!!res)
		consolidateSharedLiterals(res);
	m_dynamic = oldyn;
	return res;
}

PARSEFUNC(Index)
#define father parseOperand
BINARY_BEGIN
	bool isBracket=parseWord("[");
	if ( isBracket || parseWord("[:") ) { 
		consolidateSharedLiterals(cur);
		EPtr prev(cur);
		bool isAlt=!isBracket || parseWord(":");
		m_dynamic|=DYNAMIC_BITMASK_DYN;
		if ( parseWord("]") ) { 
			new (cur) StructNameExpr(prev);
		}
		else {
			EPtr i( parseExpression() );
			pcheck(i);
			if (isAlt)
				new (cur) IndexAltExpr(prev,i);
			else
				new (cur) IndexExpr(prev,i);
			if (!parseWord("]")) throw("Missing ']'.");
		}
	}
	  else if ( parseWord("!") )
		{ 
			consolidateSharedLiterals(cur);
			EPtr prev(cur);
			m_dynamic|=DYNAMIC_BITMASK_DYN;
		  new (cur) EvalExpr(prev);
	  }
	  else if ( parseWord(".*") )
	  { 
			consolidateSharedLiterals(cur);
			EPtr prev(cur);
			m_dynamic|=DYNAMIC_BITMASK_DYN;
			new (cur) StructElemsExpr(prev);
		}
	  else 
		{	
			bool di=parseWord(".@");
			if (di || parseWord(".") )
		  {
				consolidateSharedLiterals(cur);
				EPtr prev(cur);
				m_dynamic|=DYNAMIC_BITMASK_DYN;
		  	unsigned char c=m_s[0];
		  	if ( isIdentifier(c) )
		  	{ 
					String name((char *)m_s);
					nextWord();
		    	if (di) new (cur) StructIndexingNumberExpr(prev, name);
		      else new (cur) StructIndexingExpr(prev,name);
	    	} else if (di) 
		      	new (cur) StructLengthExpr(prev);
	    	else throw("Unexpected after '.'");
		  }
		  else if ( parseWord("->") ) { 
				consolidateSharedLiterals(cur);
				EPtr prev(cur);
				m_dynamic|=DYNAMIC_BITMASK_DYN;
				String name((char *)m_s);
		  	if ( isIdentifier(m_s[0]) )
		    	nextWord();
	    	else throw("Unexpected after '.'");
				EPtr res(parseIndependentBlock());
	    	if (!res)
		    	new (cur) HeadStructIndexingExpr(prev,name);
				else
					new (cur) HeadCallExpr(prev,res,name);
			}
			else {	
				EPtr res(parseIndependentBlock());
				if (!res) break;
				{	
					EPtr prev(cur);
					m_dynamic|=DYNAMIC_BITMASK_DYN;
			 		new (cur) ApplyExpr(res, prev);
		 		}
			}
		}
	}
	return cur;
}
ENDFUNC

//todo: this must all change as soon as real compiler comes, because then the parse tree is just stored.
PARSEFUNC(Unary)
#undef father
#define father parseIndex
#define UNARY_ITEM(i,x,expr) case i: expr; nextWord(); pcheck(f=parseUnary()); new (r) x##Expr(f); break;
#define UNARY_ITEM_DYN(i,x) UNARY_ITEM(i,x,m_dynamic|=DYNAMIC_BITMASK_DYN)
	EPtr f,r;
	if(m_s[1]==0)
	{
		switch(m_s[0])
		{
	case '&': 
		{
			int i=m_dynamic;
			m_dynamic|=DYNAMIC_BITMASK_INSIDE_REF; 
			nextWord(); operCheck(f=parseUnary()); new (r) ReferenceExpr(f); 
			m_dynamic&=~DYNAMIC_BITMASK_INSIDE_REF;
			m_dynamic|=i&DYNAMIC_BITMASK_INSIDE_REF;
			break;
		}
		UNARY_ITEM_DYN('!',Inverse    )
		UNARY_ITEM_DYN('-',Negate     )
		UNARY_ITEM_DYN('@',Length     )
		UNARY_ITEM_DYN('*',DeReference)
		UNARY_ITEM_DYN('~',BitNot     )
		default: return father();
		}
	}
	else if (parseWord("@@")) { m_dynamic|=DYNAMIC_BITMASK_DYN; pcheck(f=parseUnary()); new (r) TypeExpr(f); }
	else if (parseWord("*!")) { m_dynamic|=DYNAMIC_BITMASK_DYN; pcheck(f=parseUnary()); new (r) ValueExpr(f); }
	else return father();
	return r;
ENDFUNC

EPtr TyleParser::parseBinary(int level)
{
	EPtr cur(parseUnary());
	if ( !cur ) return EPtr();
	return parseBinaryOp(cur,level);
}
#define BINITEM(x) \
{ if (level##x<level) \
	{ \
		nextWord(); \
		consolidateSharedLiterals(cur); \
		m_dynamic|=DYNAMIC_BITMASK_DYN; \
		EPtr prev(cur); EPtr f(parseBinary(level##x)); pcheck(f); new (cur) x##Expr(prev,f); \
		break; \
	} \
}

EPtr TyleParser::parseBinaryOp(EPtr cur, int level)
{
	while (true)
	{
		switch(m_s[0])
		{
		case '&':
			if(m_s[1]==0) BINITEM(BitAnd) else if (m_s[1]=='&' && m_s[2]==0) BINITEM(And) 
			return cur;
		case '|':
			if(m_s[1]==0) BINITEM(BitOr) else if (m_s[1]=='|' && m_s[2]==0) BINITEM(Or)
			return cur;
		case '^': 
			if (m_s[1]==0) BINITEM(BitXor) 
			return cur; 
		case '=': 
			if (m_s[1]=='=' && m_s[2]==0) BINITEM(Equal) 
			return cur; 
		case '!': 
			if (m_s[1]=='=' && m_s[2]==0) BINITEM(Unequal)
			return cur; 
		case '<': 
			if (m_s[1]==0) BINITEM(Smaller) else if (m_s[2]==0) 
			{ if (m_s[1]=='=') BINITEM(SmallerEqual)
				else if (m_s[1]=='<' ) BINITEM(ShiftLeft)
			}
			return cur; 
		case '>': 
			if (m_s[1]==0) BINITEM(Greater) else if (m_s[2]==0) 
			{ if (m_s[1]=='=') BINITEM(GreaterEqual)
				else if (m_s[1]=='>') BINITEM(ShiftRight)
			}
			return cur; 
		case '+': 
			if (m_s[1]==0) BINITEM(Add) else if (m_s[2]==0) 
			{ if (m_s[1]=='>') BINITEM(Add) // deprecated
				else if (m_s[1]=='<') BINITEM(Prepend)
			}
			return cur; 
		case ':': 
			if (m_s[2]==0) 
			{ if (m_s[1]=='+') BINITEM(InsertAt)
				else if (m_s[1]=='-') BINITEM(RemoveAt)
			}
			return cur; 
		case '-': 
			if (m_s[1]==0) BINITEM(Sub)
			return cur; 
		case '*': 
			if (m_s[1]==0) BINITEM(Mul)
			return cur; 
		case '/': 
			if (m_s[1]==0) BINITEM(Div)
			return cur; 
		case '%': 
			if (m_s[1]==0) BINITEM(Mod)
			return cur; 
		case '@': 
			if (m_s[1]==0) BINITEM(FindIndex)
			return cur; 
		default: return cur;
		}
	}
}
#undef BINITEM

PARSEFUNC(Switch)
#undef father
#define father parseSwitch
BINARY_BEGIN_RIGHT(parseBinary)
	if (m_s[0]=='?') { 
		if (m_s[1]=='!' && m_s[2]==0) NEXT_CREATE_BIN_ITEM(WhileExpr)
		else if (m_s[1]==0) {
			nextWord();
			consolidateSharedLiterals(cur);
			m_dynamic|=DYNAMIC_BITMASK_DYN;
			EList l;
			EList::P lp(&l);
			do { 
				EPtr r=father();
				consolidateSharedLiterals(r);
				lp->prepend(r); lp++;
			} while (parseWord(":"));
			EPtr prev=cur;
			new (cur) SwitchOnExpr(prev, l );
		}
	}
BINARY_END_RIGHT
ENDFUNC

PARSEFUNC(Param)
#undef father
#define father parseSwitch
	EPtr res;
	EPtr *next=&res;
	int depth=0, pushes=0;
	try {
		for (;parseWord("<");depth++){
			List<TypeFunc> structFuncs;
			structing.funcs=&structFuncs;
			List<StructElem> ses;
			TypeFunc *subFunc = 0;
			TypeVar subType;
			bool commaCame=false;
			int i=0;
			TypeVar tp;
			new (tp) TypeVal();
			if (!parseWord(">")) { 
				for(;;) {
					subType =parseTypeSelection(subFunc);
					if ( (!!subFunc || !!subType) && i==0 && parseWord(">")) 
						break;
					if (!isIdentifier(m_s[0])) { 
						delete subFunc;
						throw("Expected identifier.");
					}
					String m_sc((char *)m_s);
					addStructElem(ses,tp,m_sc,subType,subFunc);i++;
					nextWord();
					bool comma=parseWord(",");
					commaCame|=comma;
					if (parseWord(">")) break;
					if (!comma) throw("Expected ',' or '>'.");
				}
			}
			if ( ( i==0 && (!!subFunc || !!subType)) || ( i==1 && !commaCame ) )
			{	SingleParamScope::Ptr scope;	
				new (scope) SingleParamScope(i==0 ? "" : ses[0].m_name , depth-session.m_funcLevel);
				session.push(scope); pushes++;
				if (!!subFunc)
					structFuncs.last().erase();
				else if (!!subType)
					subFunc = new TypeSelection(subType);
				next= & ( new (*next) SingleParamAssignmentExpr( subFunc, structFuncs, scope ) ) ->a;
			}
			else
			{	// multiple or null
				tp->set(ses,false);
				ParamSetScope::Ptr scope;
				new (scope) ParamSetScope(tp,depth-session.m_funcLevel);
				session.push(scope); pushes++;
				next = & ( new (*next) ParamSetAssignmentExpr( structFuncs, scope ) ) ->a ;	    
			}
		}
	
		*next = father();
	
		if (!*next && next!=&res)
			throw("Expected expression.");
		while(--pushes>=0) session.pop();
	} catch(const char *error) {
		while(--pushes>=0) session.pop();
			throw(error);
	}
	return res;
ENDFUNC

// right recursive:
PARSEFUNC(Iterators)
#undef father
#define father parseIterators
BINARY_BEGIN_RIGHT(parseParam)
	if (m_s[2]==0)
	{ if(m_s[1]=='!')
		{
			if (m_s[0]=='.') NEXT_CREATE_SUB_BIN_ITEM(ActOnExpr)
			else if (m_s[0]==':') NEXT_CREATE_SUB_BIN_ITEM(ActOnMultiExpr)
			else if (m_s[0]=='%') NEXT_CREATE_SUB_BIN_ITEM(ActOnColumnsExpr)
		}
		else if (m_s[0]==':' && m_s[1]=='@') NEXT_CREATE_SUB_BIN_ITEM(IndexQueryExpr)
		else if (m_s[0]=='.' && m_s[1]=='?') 
		{ int ol=session.m_funcLevel; session.m_funcLevel=1; 
			NEXT_CREATE_BIN_ITEM(ActAbstractExpr)
			session.m_funcLevel=ol;
		}
	}
BINARY_END_RIGHT
ENDFUNC

// unary
PARSEFUNC(Modify)
#undef father
#define father parseIterators
	EPtr f,r;
	if (parseWord("?")) 
	{	int ol=session.m_funcLevel; session.m_funcLevel=0; 
		int oldyn=m_dynamic; // no need to set m_dynamic to 0
		ClosureScope::Ptr scope;
		new (scope) ClosureScope(ol);
		session.push(scope);
		try {
			pcheck(f=parseModify());  
		} catch(const char *error) {
			session.pop();
			throw(error);
		}
		session.pop();
		if (((ClosureScope &)*scope).m_count)
		{	
			new (r) ClosuredExprExpr(f, scope);
			m_dynamic|=DYNAMIC_BITMASK_DYN;
		}
	  else
		{ 
			NEW_CONSTEXPR(r) ExprObject(f);
			m_dynamic=oldyn;
		}
		session.m_funcLevel=ol; 
	}
	else if (parseWord(".?")) { m_dynamic|=DYNAMIC_BITMASK_DYN; pcheck(f=parseModify()); new (r) DescribeExpr(f); }
	else return father();
	return r;
ENDFUNC

// right recursive:
PARSEFUNC(Assign)
#undef father
#define father parseAssign
BINARY_BEGIN_RIGHT(parseModify)
	BINARY_ITEM("=",AssignmentExpr )
	else if (m_s[1]=='=' && m_s[2]==0)
	{ switch(m_s[0])
		{
		case '*': NEXT_CREATE_BIN_ITEM(MulAssignExpr); break;
		case '/': NEXT_CREATE_BIN_ITEM(DivAssignExpr); break;
		case '%': NEXT_CREATE_BIN_ITEM(ModAssignExpr); break;
		case '+': NEXT_CREATE_BIN_ITEM(AddAssignExpr); break;
		case '-': NEXT_CREATE_BIN_ITEM(SubAssignExpr); break;
		case '|': NEXT_CREATE_BIN_ITEM(BitOrAssignExpr);  break;
		case '&': NEXT_CREATE_BIN_ITEM(BitAndAssignExpr); break;
		case '^': NEXT_CREATE_BIN_ITEM(BitXorAssignExpr); break;
		default: break;
		}
	}
	else if (m_s[2]=='=' && m_s[3]==0)
	{      if( m_s[0]=='>' && m_s[1]=='>' ) NEXT_CREATE_BIN_ITEM(ShiftRightAssignExpr )
		else if( m_s[0]=='<' && m_s[1]=='<' ) NEXT_CREATE_BIN_ITEM(ShiftLeftAssignExpr )
		else if( m_s[0]=='+' && m_s[1]=='>' ) NEXT_CREATE_BIN_ITEM(AddAssignExpr ) // deprecated
		else if( m_s[0]=='+' && m_s[1]=='<' ) NEXT_CREATE_BIN_ITEM(PrependAssignExpr )
		else if( m_s[0]==':' && m_s[1]=='+' ) NEXT_CREATE_BIN_ITEM(InsertAtAssignExpr )
		else if( m_s[0]==':' && m_s[1]=='-' ) NEXT_CREATE_BIN_ITEM(RemoveAtAssignExpr )
		else if( m_s[0]=='&' && m_s[1]=='&' ) NEXT_CREATE_BIN_ITEM(AndAssignExpr )
		else if( m_s[0]=='|' && m_s[1]=='|' ) NEXT_CREATE_BIN_ITEM(OrAssignExpr )
	}
BINARY_END_RIGHT
ENDFUNC

EPtr TyleParser::parseSequence()
{
#undef father
#define father parseExpression
	int oldDynamic=m_dynamic;
	m_dynamic=0;
	EPtr res;
	SequenceExpr *s=new (res) SequenceExpr();
	LocalVarVectorQueue::Ptr queue;
	new (queue) LocalVarVectorQueue();
	session.push(queue);
	try {
		bool hasSemicolons = false;
		SequenceExpr::P sp(&(s->m_elements));
		bool kr;
		do {
			kr = false;
			EPtr j=father();
			if (!!j) { sp->prepend(j); sp++; kr = true;}
			if (parseWord(",")) { // this is actually a list
				EList el; 
				el.take(s->m_elements);
				ListExpr *l=new (res) ListExpr();
				ListExpr::P ep(&(l->m_elements));
				FOREACH(EList, el, i) {
					ep->prepend(ListExpr::Elem(*i,kr&&!i.next())); ep++;
				}
				for(;;) {
					EPtr j=father();
					if ( parseWord(",")) { if(!!j) {ep->prepend(ListExpr::Elem(j,true)); ep++; } }
					else if (parseWord(";")) {
						hasSemicolons = true;
						if(!!j) {ep->prepend(ListExpr::Elem(j,false)); ep++; } 
					}
					else {
						if(!!j) {ep->prepend(ListExpr::Elem(j,true)); ep++; } 
						break; 
					}
				}
				if (hasSemicolons && ( m_dynamic&DYNAMIC_BITMASK_SHAREDLITERAL) )
					throw("Cannot use ';' somewhere in a literal expression");
				session.lvvqPop();
				l->setQueue(queue);
				if (!m_dynamic) {
					Object *o=res->evaluateToPersistCopy();
					new (res) ConstExpr(o);
					m_dynamic = oldDynamic;
				}
				return res;
			}
			if (!parseWord(";"))
				break;
			hasSemicolons = true;
		} while (true);
		s->keepResult=kr;
		if (!s->m_elements) {
			res = session.m_exprNull;
			session.lvvqPop();
		}
		else if ( !session.lvvqPop() && s->m_elements.length()==1 && kr ) {
			res = *(s->m_elements);
		} else {
			if (m_dynamic&DYNAMIC_BITMASK_SHAREDLITERAL)
				throw("Cannot use ';' somewhere in a literal expression");
			s->setQueue(queue);
		}
		m_dynamic |= oldDynamic;
	} catch(const char *error) {
		session.lvvqPop();
		throw(error);
	}
	return res;
}

EPtr TyleParser::finishParseList()
#undef father
#define father parseExpression
{ 
	int oldDynamic=m_dynamic;
	m_dynamic=0;
	EPtr res;
	ListExpr *s=new (res) ListExpr();
	LocalVarVectorQueue::Ptr queue;
	new (queue) LocalVarVectorQueue();
	session.push(queue);
	bool hasSemicolons=false;
	try
	{
		ListExpr::P ep(&(s->m_elements));
		while(!parseWord("}"))
		{	EPtr j=father();
			if ( parseWord("}"))     { if(!!j) {ep->prepend(ListExpr::Elem(j,true)); ep++; } break; }
			if ( parseWord(","))     { if(!!j) {ep->prepend(ListExpr::Elem(j,true)); ep++; } }
			else if (parseWord(";")) { 
				hasSemicolons = true;
				if(!!j) {ep->prepend(ListExpr::Elem(j,false)); ep++; } 
			}
			else throw("Expected ',',';' or '}'.");
		}
		if (hasSemicolons && (m_dynamic&DYNAMIC_BITMASK_SHAREDLITERAL)) {
			throw("Cannot use ';' somewhere in a literal expression");
		}
		session.lvvqPop();
		s->setQueue(queue);
		if (!m_dynamic)
		{	
			if (!s->m_elements)
				res = session.m_exprEmptyList;
			else
			{	Object *o=res->evaluateToPersistCopy();
				new (res) ConstExpr(o);
			}
			m_dynamic = oldDynamic;
		}
	} catch(const char *error) {
		session.lvvqPop();
		throw(error);
	}
	return res;
}

EPtr TyleParser::finishParseSequence()
{
	EPtr res;
	res = parseSequence();
	if (!parseWord(")")) throw("Expected ')'.");
	return res;
}

EPtr TyleParser::finishMap()
{
	int oldDynamic=m_dynamic;
	m_dynamic=0;

	EPtr res;
	MapExpr *s=new (res) MapExpr();

	List<EPtr>::P lp(&(s->m_elements));

	for(;;)
	{ 
		EPtr a(parseExpression());
		if (!a)
		{ if (!parseWord("}")) throw("Expected '}'");
		  break;
		}
		lp->prepend(a);lp++;
		EPtr b;
		if ( parseWord("=>")) 
		{ b=parseExpression();
		  if (!b) throw("Expected a expression");
		}  
		lp->prepend(b);lp++; 
		if ( parseWord("}")) break;
		if ( !parseWord(",")) throw("Expected ','");
	}
	if (!m_dynamic)
	{	
		if (!s->m_elements)
			res = session.m_exprEmptyMap;
		else
		{	
			Object *o=res->evaluateToPersistCopy();
			new (res) ConstExpr(o);
		}
		m_dynamic = oldDynamic;
	}
	return res;
}

PARSEFUNC(Block)
	EPtr res;
	if (parseWord("{")) res = finishParseList();
	else if (parseWord("(")) res = finishParseSequence();
	else if ( parseWord("{:") ) res = finishMap();
	return res;
ENDFUNC
